package singleflight

import "sync"

// 代表正在进行中，或已经结束的请求。使用sync.WaitGroup锁避免重入。
// Group 是singleflight的主数据结构，管理不同key的请求（call）
type call struct {
	wg  sync.WaitGroup
	val interface{}
	err error
}

type Group struct {
	mu sync.Mutex
	m  map[string]*call
}

// Do方法，接收一个参数是key，第二个参数是一个函数fn。Do的作用就是，针对
//相同的key，无论Do被调用多少次，函数fn都会只调用一次，等待fn调用结束了，
//返回返回返回值或错误
// g.mu 是保护Group的成员变量m不被并发读写而加上锁。为了便于理解Do函数，我们
// 将g.mu暂时去掉。并且把g.m延迟初始化的部分去掉，延迟初始化的目的很简单，
// 提高内存使用效率
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
	g.mu.Lock()
	if g.m == nil {
		g.m = make(map[string]*call)
	}
	if c, ok := g.m[key]; ok {
		g.mu.Unlock()
		c.wg.Wait()
		return c.val, c.err
	}
	c := new(call)
	c.wg.Add(1)
	g.m[key] = c
	g.mu.Unlock()

	c.val, c.err = fn()
	c.wg.Done()

	g.mu.Lock()
	delete(g.m, key)
	g.mu.Unlock()

	return c.val, c.err
}
